/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.servlet.api.javax;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import com.jphenix.servlet.filter.BaseFilter;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.beans.IKernel;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.IResponseManager;
import com.jphenix.standard.servlet.api.ICookie;
import com.jphenix.standard.servlet.api.IRequest;
import com.jphenix.standard.servlet.api.ServletOutputStream;

/**
 * 封装 页面返回类
 * @author 刘虻
 * 2007-1-9下午02:10:17
 * 2019-09-21 可以在配置文件中增加<base_host>http://网站根url（不包含虚拟路径）</base_host>，避免Host头信息被篡改，导致重定向到非法网站漏洞
 * 2021-03-17 适配tomcat9中的 servlet-api.jar
 * 2022-09-04 重构了对接Tomcat9以及以前版本代码
 * 2022-09-05 修改了发现的错误
 * 
 * 最后修改:
 * 
 * 	08-11-18 sendRedirect 
 * 方法有时抛出异常response already committed 此异常通常不错处理,故屏蔽掉
 */
@ClassInfo({"2022-09-07 13:09","封装 页面返回类"})
public class HttpServletResponseImpl implements IResponseManager,IKernel,HttpServletResponse {

	protected HttpServletResponse kernel            = null;    // 核心类实例
	private String                characterEncoding = "UTF-8"; // 编码格式
	private Map<String,String>    headerMap         = null;    // 头信息
	private int                   status            = 200;     // 状态
	private Locale                locale            = null;    // 国际化类
	private ServletOutputStream   bufferOs          = null;    // 缓存对象
	private PrintWriter           bufferPw          = null;    // 缓存输出类
	private ByteArrayOutputStream bos               = null;    // 字节输出流
	private String                contentType       = null;    // 内容类型 
	private String                errorMsg          = null;    // 返回到页面的错误信息
	private Map<String,String>    errorPageMap      = null;    // 错误代码对照重定向文件对照容器
	private BaseFilter            fe                = null;    // 过滤器入口
	
	//是否为假的页面反馈
	//用clone或者ewInstance方法 构造的当前类，该值为true
	private boolean               isFake            = false;

	/**
	 * 构造函数
	 * 2007-5-24上午09:56:29
	 */
	public HttpServletResponseImpl(
			HttpServletResponse httpServletResponse) {
		super();
		if(httpServletResponse==null) {
			isFake    = true;
			headerMap = new HashMap<String,String>();
			bos       = new ByteArrayOutputStream();
			return;
		}
		this.kernel = httpServletResponse;
	}
	
	/**
	 * 构造函数
	 * @author MBG
	 */
	public HttpServletResponseImpl(
			ServletResponse servletResponse) {
		super();
		if(servletResponse==null) {
			isFake    = true;
			headerMap = new HashMap<String,String>();
			bos       = new ByteArrayOutputStream();
			return;
		}
		this.kernel = (HttpServletResponse)servletResponse;
	}
	
	/**
	 * 构造函数
	 * 2007-5-24上午09:56:29
	 */
	public HttpServletResponseImpl(
			HttpServletResponse httpServletResponse,BaseFilter fe,Map<String,String> errorPageMap) {
		super();
		if(httpServletResponse==null) {
			isFake        = true;
			headerMap     = new HashMap<String,String>();
			bos           = new ByteArrayOutputStream();
			return;
		}
		this.kernel       = httpServletResponse;
		this.errorPageMap = errorPageMap;
		this.fe           = fe;
	}
	
	/**
	 * 构造函数
	 * @author MBG
	 */
	public HttpServletResponseImpl(
			ServletResponse servletResponse,BaseFilter fe,Map<String,String> errorPageMap) {
		super();
		if(servletResponse==null) {
			isFake        = true;
			headerMap     = new HashMap<String,String>();
			bos           = new ByteArrayOutputStream();
			return;
		}
		this.kernel       = (HttpServletResponse)servletResponse;
		this.errorPageMap = errorPageMap;
		this.fe           = fe;
	}
	
	/**
	 * 构造函数
	 * 2007-5-24上午09:56:29
	 */
	public HttpServletResponseImpl(
			HttpServletResponse httpServletResponse,boolean isFake) {
		super();
		if(httpServletResponse==null) {
			this.isFake            = true;
		}else {
			this.isFake            = isFake;
			this.kernel            = httpServletResponse;
			this.characterEncoding = kernel.getCharacterEncoding();
			this.locale            = kernel.getLocale();
			this.contentType       = kernel.getContentType();
		}
		if(isFake) {
			headerMap = new HashMap<String,String>();
			bos       = new ByteArrayOutputStream();
		}
	}
	
	/**
	 * 获取核心类
	 * @return 核心类
	 * 2014年12月3日
	 * @author 马宝刚
	 */
	@Override
    public Object getKernel() {
	    return kernel;
	}
	
    /**
     * 创建cookie
     * @param name  名字
     * @param value 值
     * @return cookie
     * 2022年8月27日
     * @author MBG
     */
	@Override
    public ICookie createCookie(String name,String value) {
    	return new CookieImpl(new Cookie(name,value));
    }
	
	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void addCookie(ICookie cookie) {
		if(isFake) {
			return;
		}
		kernel.addCookie((Cookie)((IKernel)cookie).getKernel());
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void addDateHeader(String arg0, long arg1) {
		if(isFake) {
			//获取原有值
			String value = SString.valueOf(headerMap.get(arg0));
			if(value.length()>0) {
				value += ",";
			}
			value += String.valueOf(arg1);
			headerMap.put(arg0,value);
			return;
		}
		kernel.addDateHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void addHeader(String arg0, String arg1) {
		if(isFake) {
			//获取原有值
			String value = SString.valueOf(headerMap.get(arg0));
			if(value.length()>0) {
				value += ",";
			}
			value += arg1;
			headerMap.put(arg0,value);
			return;
		}
		kernel.addHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void addIntHeader(String arg0, int arg1) {
		if(isFake) {
			//获取原有值
			String value = SString.valueOf(headerMap.get(arg0));
			if(value.length()>0) {
				value += ",";
			}
			value += String.valueOf(arg1);
			headerMap.put(arg0,value);
			return;
		}
		kernel.addIntHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public boolean containsHeader(String arg0) {
		if(isFake) {
			return headerMap.containsKey(arg0);
		}
		return kernel.containsHeader(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public String encodeRedirectURL(String arg0) {
		if(isFake) {
			return "";
		}
		return kernel.encodeRedirectURL(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public String encodeURL(String arg0) {
		if(isFake) {
			return StringUtil.getURLEncoding(arg0);
		}
		return kernel.encodeURL(arg0);
	}

    /**
     * 向浏览器输出错误信息，并跳转到指定的错误页面
     * 程序会解析错误页面中的动态内容
     * @param status          错误代码
     * @param req             页面请求（用于解析错误页面中的动态内容）
     * @throws IOException    异常
     * 2017年11月24日
     * @author MBG
     */
	@Override
    public void sendError(int status, IRequest req) throws IOException {
		this.status = status;
		if(isFake) {
			return;
		}
		setStatus(status);
		if(req!=null) {
			req.setAttribute("_http_error_status",String.valueOf(status));
		}
		if(errorPageMap!=null && fe!=null) {
			//尝试获取内部错误提示文件
			String path = errorPageMap.get(String.valueOf(status));
			if(path!=null && fe!=null) {
				try {
					if(fe.serveFile(path,req,this)) {
						return;
					}
				}catch(Exception e) {}
			}
		}
		kernel.sendError(status);
	}

    /**
     * 向浏览器输出错误信息，并跳转到指定的错误页面
     * 程序会解析错误页面中的动态内容
     * @param status          错误代码
     * @param errorMsg        错误信息
     * @param req             页面请求（用于解析错误页面中的动态内容）
     * @throws IOException    异常
     * 2017年11月24日
     * @author MBG
     */
	@Override
    public void sendError(int status, String errorMsg, IRequest req) throws IOException {
		this.status = status;
		this.errorMsg = errorMsg;
		if(isFake) {
			return;
		}
		setStatus(status);
		if(req!=null) {
			req.setAttribute("_http_error_status",String.valueOf(status));
			req.setAttribute("_http_error_msg",errorMsg);
		}
		if(errorPageMap!=null && fe!=null) {
			//尝试获取内部错误提示文件
			String path = errorPageMap.get(String.valueOf(status));
			if(path!=null && fe!=null) {
				try {
					if(fe.serveFile(path,req,this)) {
						return;
					}
				}catch(Exception e) {}
			}
		}
		kernel.sendError(status,errorMsg);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void sendError(int arg0) throws IOException {
		status = arg0;
		if(isFake) {
			return;
		}
		setStatus(arg0);
		kernel.sendError(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void sendError(int status, String errorMsg) throws IOException {
		this.status   = status;
		this.errorMsg = errorMsg;
		if(isFake) {
			return;
		}
		setStatus(status);
		kernel.sendError(status,errorMsg);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void sendRedirect(String arg0) throws IOException {
		if(arg0==null) {
			return;
		}
		if(isFake) {
			return;
		}
		//如果重定向到相对路径，Servlet容器会从请求头中Host值作为根路径来拼装成完整路径
		//如果Host值在请求前被篡改，就会存在重定向到冒仿网站的可能
		//所以需要在配置文件中配置网站根路径，避免这种可能
		if(arg0.indexOf("://")>0) {
			try {
				kernel.sendRedirect(arg0);
			}catch(Exception e) {}
			return;
		}
		if(fe!=null) {
			//网站根路径（不包含虚拟路径）
			String baseUrl = fe.getBeanFactory().getConfProp().getParameter("base_host");
			if(baseUrl.length()>0) {
				arg0 = baseUrl+arg0;
			}
		}
		try {
			kernel.sendRedirect(arg0);
		}catch(Exception e) {}
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setDateHeader(String arg0, long arg1) {
		if(isFake) {
			headerMap.put(arg0,String.valueOf(arg1));
			return;
		}
		kernel.setDateHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setHeader(String arg0, String arg1) {
		if(isFake) {
			headerMap.put(arg0,arg1);
			return;
		}
		kernel.setHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setIntHeader(String arg0, int arg1) {
		if(isFake) {
			headerMap.put(arg0,String.valueOf(arg1));
			return;
		}
		kernel.setIntHeader(arg0,arg1);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setStatus(int arg0) {
		if(isFake) {
			status = arg0;
			return;
		}
		kernel.setStatus(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void flushBuffer() throws IOException {
		if(bufferOs!=null) {
			try {
				bufferOs.flush();
			}catch(Exception e) {}
		}
		if(bufferPw!=null) {
			try {
				bufferPw.flush();
			}catch(Exception e) {}
		}
		if(bufferOs==null && bufferPw==null) {
			try {
				kernel.flushBuffer();
			}catch(Exception e) {}
		}
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public int getBufferSize() {
		if(isFake) {
			return bos.size();
		}
		return kernel.getBufferSize();
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public String getCharacterEncoding() {
		if(characterEncoding==null && kernel!=null) {
			characterEncoding = kernel.getCharacterEncoding();
		}
		return characterEncoding;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public Locale getLocale() {
		if(locale==null && kernel!=null) {
			locale = kernel.getLocale();
		}
		return locale;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public ServletOutputStream iGetOutputStream() throws IOException {
		if(isFake) {
			if(bufferOs==null) {
				bufferOs = new ServletOutputStream(bos,kernel.getCharacterEncoding());
			}
			return bufferOs;
		}else if(bufferOs==null) {
			bufferOs = new ServletOutputStream(kernel.getOutputStream(),kernel.getCharacterEncoding());
		}
		return bufferOs;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public PrintWriter getWriter() throws IOException {
		if(isFake) {
			if(bufferPw==null) {
				bufferPw = new PrintWriter(bos);
			}
			return bufferPw;
		}else if(bufferPw==null) {
			bufferPw = kernel.getWriter();
		}
		return bufferPw;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public boolean isCommitted() {
		if(isFake) {
			return true;
		}
		return kernel.isCommitted();
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void reset() {
		if(isFake) {
			bos.reset();
			return;
		}
		kernel.reset();
	}
	
	/**
	 * 清除缓存
	 * 2017年4月28日
	 * @author MBG
	 */
	@Override
    public void clear() {
		bufferOs = null;
		bufferPw = null;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void resetBuffer() {
		if(isFake) {
			bos.reset();
			return;
		}
		kernel.resetBuffer();
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setBufferSize(int arg0) {
		if(isFake) {
			return;
		}
		kernel.setBufferSize(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setContentLength(int arg0) {
		if(isFake) {
			return;
		}
		kernel.setContentLength(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setContentType(String arg0) {
		if(isFake) {
			contentType = arg0;
			return;
		}
		kernel.setContentType(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-1-9下午02:10:17
	 */
	@Override
    public void setLocale(Locale arg0) {
		if(isFake) {
			locale = arg0;
			return;
		}
		kernel.setLocale(arg0);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-3-13下午08:41:06
	 */
	@Override
    public String getContentType() {
		if(isFake) {
			return contentType;
		}
		return kernel.getContentType();
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2007-3-13下午08:41:17
	 */
	@Override
    public void setCharacterEncoding(String charset) {
		if(isFake) {
			characterEncoding = charset;
			return;
		}
		kernel.setCharacterEncoding(charset);
	}



    @Override
    public String getHeader(String arg0) {
    	if(isFake) {
    		return SString.valueOf(headerMap.get(arg0));
    	}
        return kernel.getHeader(arg0);
    }



    @Override
    public Collection<String> getHeaderNames() {
    	if(isFake) {
    		return BaseUtil.getMapKeyList(headerMap);
    	}
        return kernel.getHeaderNames();
    }

    @Override
    public Collection<String> getHeaders(String arg0) {
    	if(isFake) {
    		return BaseUtil.splitToList(headerMap.get(arg0),",");
    	}
        return kernel.getHeaders(arg0);
    }

    @Override
    public int getStatus() {
    	if(isFake) {
    		return status;
    	}
        return kernel.getStatus();
    }
    
    /**
     * 返回错误信息，如果有的话
     * @return 错误信息
     * 2017年3月1日
     * @author MBG
     */
    @Override
    public String getErrorMsg() {
    	if(errorMsg==null) {
    		return "";
    	}
    	return errorMsg;
    }
    
	/**
	 * 将信息头放入返回对象
	 * @param headerMap 信息头容器
	 * 2016年7月13日
	 * @author MBG
	 */
	@Override
    public void putHeaderMap(Map<String,String> headerMap) {
		if(headerMap==null) {
			return;
		}
		//获取主键序列
		List<String> keyList = BaseUtil.getMapKeyList(headerMap);
		for(String key:keyList) {
			setHeader(key,SString.valueOf(headerMap.get(key)));
		}
	}
	
	/**
	 * 获取返回的信息数据流
	 * @return 返回的信息数据流
	 * 2016年11月25日
	 * @author MBG
	 */
	@Override
    public ByteArrayOutputStream getData() {
		return bos;
	}
	
	/**
	 * 覆盖方法
	 */
	@Override
    public String toString() {
		if(isFake) {
			return bos.toString();
		}
		if(kernel!=null) {
			return kernel.toString();
		}
		return this.getClass().getName()+"_"+hashCode();
	}

	/**
	 * 覆盖方法
	 */
	@Override
	public IResponseManager newInstance() {
		return new HttpServletResponseImpl(kernel,true);
	}

	/**
	 * For Tomcat8
	 * @param arg0
	 * 2018年1月18日
	 * @author MBG
	 */
	public void setContentLengthLong(long arg0) {}

	/**
	 * 覆盖方法
	 */
	@Override
	public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
		return kernel.getOutputStream();
	}

	/**
	 * 覆盖方法
	 */
	@Override
	public void addCookie(Cookie cookie) {
		kernel.addCookie(cookie);
	}

	/**
	 * 覆盖方法
	 */
	@Override
	@Deprecated
	public String encodeUrl(String url) {
		return kernel.encodeUrl(url);
	}

	/**
	 * 覆盖方法
	 */
	@Override
	@Deprecated
	public String encodeRedirectUrl(String url) {
		return kernel.encodeRedirectUrl(url);
	}

	/**
	 * 覆盖方法
	 */
	@Override
	@Deprecated
	public void setStatus(int sc, String sm) {
		kernel.setStatus(sc,sm);
	}
}
